import random
import re
import numpy as np

dt = np.dtype([('type', 'i1'), ('level', 'i1')])

type_list = ['b', 'd', 'y', 'q', 'e', 'g', 'r', 'p']
chance_list = [
    [10, 0, 0, 0, 0], [8, 2, 0, 0, 0], [6, 3, 1, 0, 0],
    [4, 3, 2, 1, 0], [1, 3, 3, 2, 1]
]
level_list = [6, 11, 16, 21, 51]
l1 = ['35', '猴子', '祖母', '祖母',  '精雕', '黄彩', 'e1', 'e2', 'e3', 'e4', 'e5']
l2 = ['38', '骑士', '鸡', '猴子', '祖母', '祖母',
      'e1', 'e2', 'e3', 'e4', 'e5', '精雕', '黄彩']
l3 = ['巨粉', '鸡', '猴子', '珊瑚', 'e1', 'e2', 'e3', '精雕', '黄彩']
l4 = ['35', '鸡', '猴子', '珊瑚', 'e1', 'e2', 'e3', '精雕', '黄彩']
combine = {'白银': ('b1', 'd1', 'y1'), '孔雀': ('q1', 'e1', 'g1'), '星彩': ('r2', 'r1', 'p1'),
           '玉': ('e3', 'g3', 'b2'), '芙蓉': ('g4', 'r3', 'p2'), '翡翠': ('g5', 'b4', 'y2'),
           '黄彩': ('b5', 'y4', 'r4'), '黄金': ('p5', 'p4', 'd2'), '粉钻': ('d5', 'd3', 'y3'),
           '鸡血': ('r5', 'q4', 'p3'), '38': ('y5', 'b3', 'e2'), '猫眼': ('e5', 'd4', 'q3'),
           '碧玺': ('q5', 'e4', 'g2'), '珍珠': ('q4', 'd4', 'e2'), '黑曜': ('b5', 'd5', 'y5'),
           '玛瑙': ('e5', 'g5', 'q5'), '虾妹': ('b5', 'g5', 'r5'), '陨石': ('r5', 'p5', 'r4', 'p4'),
           '鸽卵': ('b5', 'g5', 'b4', 'g4'), '金刚': ('d1', 'd2', 'd3', 'd4', 'd5'), '陛下': ('y1', 'y2', 'y3', 'y4', 'y5'),
           '皇冠': ('r1', 'r2', 'r3', 'r4', 'r5'), '兰卡': ('g1', 'g2', 'g3', 'g4', 'g5'), '星光': ('b1', 'b2', 'b3', 'b4', 'b5'),
           '卡门': ('e1', 'e2', 'e3', 'e4', 'e5'), '衰败': ('q1', 'q2', 'q3', 'q4', 'q5'), '光明': ('p1', 'p2', 'p3', 'p4', 'p5')}
combine2 = {'骑士': ('白银', 'r3', 'q2'), '巨粉': ('骑士', '白银', '粉钻'), '大蛇': ('孔雀', 'y3', 'd2'),
            '35': ('38', '孔雀', '大蛇'), '火山': ('星彩', 'r4', 'p3'), '鸡': ('火山', '鸡血', 'r2'),
            '青玉': ('玉', 'b4', 'q3'), '猴子': ('青玉', 'g4', 'p2'), '国玉': ('玉', '芙蓉', 'g3'),
            '蝴蝶': ('芙蓉', 'p4', 'y2'), '埃及': ('黄金', 'p5', 'q3'), '珊瑚': ('珍珠', '猫眼', 'e4'),
            '豹眼': ('黄彩', '鸡血', 'b5'), '魔像': ('翡翠', '黄金', 'd3'), '精雕': ('碧玺', '翡翠', 'g2'),
            '祖母': ('珍珠', 'g5', 'd3'), '光明': ('巨粉', 'd6', 'p6'), '衰变': ('35', 'q6', 'y6'),
            '皇冠': ('鸡', 'r6', 'g6'), '金刚': ('猴子', 'd6', 'b6'), '陛下': ('蝴蝶', 'y6', 'r6'),
            '兰卡': ('精雕', 'g6', 'p6'), '卡门': ('珊瑚', 'e6', 'q6'), '星光': ('黄彩', 'b6', 'e6')}
combine3 = {'诱捕': ('y3', 'd2'), '瘴气': ('g3', 'e2'), '洪流': ('b3', 'q2'), '嗥叫': ('p3', 'r2'),
            '酸雾': ('q3', 'y2'), '麻痹': ('r3', 'g2'), '恐吓': ('d3', 'p2'), '虚无': ('e3', 'b2')}


class Tower_base():
    l = 1
    cl = chance_list[0]

    def __init__(self, type=0, level=1):
        self.base = (type, level)

    @staticmethod
    def tower(s):
        return Tower_base(*Tower_base.to_base(s))

    @staticmethod
    def set_wave(level):
        Tower_base.l = level
        for i in range(5):
            if level < level_list[i]:
                Tower_base.cl = chance_list[i]
                break

    @staticmethod
    def random_ceate():
        level = random.choices(range(1, 6), Tower_base.cl)[0]
        type = random.randint(0, 7)
        return (type, level)

    @staticmethod
    def one_term(v):
        Tower_base.set_wave(v)
        li = []
        for i in range(5):
            li.append(Tower_base.random_ceate())
        return li

    def __str__(self):
        return f'{type_list[self.base[0]]}{self.base[1]}'

    @staticmethod
    def hu(oneterm):
        hulist = []
        for i in combine:
            if set(Tower_base.to_base_list(combine[i])) < set(oneterm):
                hulist.append(i)
        shiban = []
        for i in combine3:
            if set(Tower_base.to_base_list(combine3[i])) < set(oneterm):
                shiban.append(i)

        d = oneterm[:]
        for i in d:
            con = d.count(i)
            if con == 1:
                d.remove(i)
            elif con > 3:
                oneterm.append((i[0], i[1]+2))
                oneterm.append((i[0], i[1]+1))
                break
            elif 1 < con:
                d.remove(i)
                d.remove(i)
                oneterm.append((i[0], i[1]+1))
            else:
                print('hu bug', oneterm)

        return(oneterm, hulist, shiban)

    @staticmethod
    def hu_all(arr):
        hulist = {}
        shiban = {}
        x = 0

        for y in arr:
            x += 1
            for i in combine:
                if set(Tower_base.to_base_list(combine[i])) < set(y):
                    hulist[x] = i

        x = 0
        for y in arr:
            x += 1
            for i in combine3:
                if set(Tower_base.to_base_list(combine3[i])) < set(y):
                    if x in shiban:
                        shiban[x].append(i)
                    else:
                        shiban[x] = [i]

        for lis in arr:
            d = [p for p in lis]
            for i in d:
                con = d.count(i)
                if con == 1:
                    d.remove(i)
                elif con > 3:
                    lis.append((i[0], i[1]+2))
                    lis.append((i[0], i[1]+1))
                    break
                elif 1 < con:
                    d.remove(i)
                    d.remove(i)
                    lis.append((i[0], i[1]+1))
                else:
                    print('hu bug', lis)
        return arr, hulist, shiban

    @staticmethod
    def to_base(s: str):
        '''trans 'b5' to (0,5) '''
        return (type_list.index(s[0]), int(s[1]))

    @staticmethod
    def to_base_list(str_tuple):
        ''' trans ('b5','y4','r4') to [(0,5),(2,4),(6,4)] '''
        return [Tower_base.to_base(i) for i in str_tuple]

    @staticmethod
    def print_list(l):
        for j in l:
            print(to_str(j), end=',')
        print('\n')

    @staticmethod
    def query(name: str):
        if name in combine2:
            return combine2[name]
        elif name in combine:
            return combine[name]
        else:
            print('query failed:', name)

    @staticmethod
    def count_base(need_list, r=[], pa=re.compile(r'[bdyqegrp][1-7]')):
        for i in need_list:
            if pa.match(i):
                r.append(i)
            else:
                Tower_base.count_base(Tower_base.query(i), r, pa)
        return r


def check_combine(l):
    ready_combine=[]
    for i in combine:
        if set(Tower_base.to_base_list(combine[i])) < set(l):
            l.append(i)


def to_str(tuple):
    if tuple == None:
        return 'None'

    return f'{type_list[tuple[0]]}{tuple[1]}'


def to_np(l):
    '''['b2','e3','g3'] to array[(0,2)(4,3)(5,3)]'''
    x = Tower_base.to_base_list(l)
    return np.array(x, dtype=dt)


def game():
    while 1:
        a = input('1,开始\n2,退出\n')
        if a == '1':
            print('game start!')
            l = []
            n = 0
            while n < 51:
                print(f'wave:{n}:')
                for i in l:
                    print(i, end=',')
                print('\n')
                n += 1
                li = Tower_base.one_term(n)
                for i in li:
                    print(i, end=',')
                print('\n')
                l.append(li[int(input('select\n'))])
        else:
            break


def god_select(target, towerlist):
    tower_dict = {}
    for i in range(len(towerlist)):
        tower_dict[i] = towerlist[i]
    select = [None]*50
    selected = []
    not_found = []
    while len(target) > 0:
        flag = 0
        i = target[-1]
        for j in tower_dict:
            if i in tower_dict[j]:
                print(target[-1], 'foundd in :wave', j+1)
                select[j] = i
                selected.append([j, i, tower_dict[j], 0])
                target.pop(-1)
                tower_dict.pop(j)
                flag = 1
                break
        if flag == 0:
            for j in selected:
                if i in j and j[3] == 0:
                    print(target[-1], 'found in selected in:', j)
                    target.pop(-1)
                    select[j[0]] = i
                    selected.append([j, i, j[2], 1])
                    selected.remove(j)
                    flag = 1
                    target.append(j[1])
                    break
        if flag == 0:
            not_found.append(i)
            target.pop(-1)
            print(i, 'not found!')

        with open('simulate.txt', 'w') as f:
            for i in range(len(towerlist)):
                f.write(f'wave: {i}:')
                for j in towerlist[i]:
                    f.write(to_str(j)+' ')
                f.write(':'+to_str(select[i])+'\n')

    return select, selected, not_found


def whether_worth_hu(hu, targetlist):
    ''' if 2 or more towers of hu in targetlist,
        return True and remove them from list.\n
        if only one or no one,return False'''
    num = 0
    l = []
    for i in combine[hu]:
        if i in targetlist:
            l.append(i)
            num += 1
    if num > 1:
        for t in l:
            targetlist.remove(t)
        return True
    else:
        return False


def select_by_level(target, targetlist=[]):
    selected = []
    if targetlist == []:
        a = to_np(Tower_base.count_base(target))
        a.sort(order='level')
        targetlist = a.tolist()
    for i in range(1, 51):
        hu, shiban, l = Tower_base.hu(Tower_base.one_term(i))
        flag_hu = False
        for t in hu:
            if t in target:
                flag_hu = whether_worth_hu(t, targetlist)
                if whether_worth_hu:
                    selected.append(t)
                    target.remove(t)
        if not flag_hu:
            pass
        # TODO


def test(targetlist):
    a = to_np(Tower_base.count_base(targetlist))
    a.sort(order='level')
    target = a.tolist()
    print('total need:', len(target))
    l = []
    for i in range(1, 51):
        l.append(Tower_base.one_term(i))
    r, hu, shiban = Tower_base.hu_all(l)
    # r = np.array(r)
    # print(r)
    print(hu)
    return god_select(target, r)
    # print(shiban)
    # print(a)


if __name__ == "__main__":
    # a = Tower_base.count_base(l4)
    # # a.sort()
    # b = to_np(a)
    # b[::-1].sort(order='level')
    # uni, count = np.unique(b, return_counts=True)
    # print(uni[np.argsort(-count)])
    select, selected, not_found = test(l1)
    Tower_base.print_list(select)
    Tower_base.print_list(not_found)
    print('short :', len(not_found))
    # print(Tower_base.tower('b1').base)
